

// Token信息接口定义，描述了令牌相关的数据结构
export interface TokenInfo {
  accessToken: string;       // 访问令牌，用于API请求授权
  refreshToken: string;      // 刷新令牌，用于在访问令牌过期时获取新的访问令牌
  accessTokenExpiry: number; // 访问令牌过期时间戳（毫秒）
  refreshTokenExpiry: number; // 刷新令牌过期时间戳（毫秒）
}

// 会话用户信息接口定义，描述了用户相关的数据结构
export interface SessionUserInfo {
  id: number;            // 用户ID
  name: string;          // 用户名称
  role: string;          // 用户角色
  mfaVerified: boolean;  // 是否已通过多因素认证
}

// TokenManager类，负责管理用户认证令牌的生成、存储、验证和刷新
class TokenManager {
  private static instance: TokenManager; // 单例实例
  // 本地存储中用于保存令牌和用户信息的键名
  private readonly ACCESS_TOKEN_KEY = 'access_token';          // 访问令牌存储键
  private readonly REFRESH_TOKEN_KEY = 'refresh_token';        // 刷新令牌存储键
  private readonly ACCESS_TOKEN_EXPIRY_KEY = 'access_token_expiry'; // 访问令牌过期时间存储键
  private readonly REFRESH_TOKEN_EXPIRY_KEY = 'refresh_token_expiry'; // 刷新令牌过期时间存储键
  private readonly USER_INFO_KEY = 'user_info';                // 用户信息存储键
  private readonly ACCESS_TOKEN_TTL = 15 * 60 * 1000;          // 访问令牌有效期：15分钟
  private readonly REFRESH_TOKEN_TTL = 7 * 24 * 60 * 60 * 1000; // 刷新令牌有效期：7天
  private refreshTimeout: number | null = null;                // 自动刷新定时器ID

  // 私有构造函数，防止外部直接实例化
  private constructor() { /* empty */ }

  // 单例模式实现，确保全局只有一个TokenManager实例
  public static getInstance(): TokenManager {
    if (!TokenManager.instance) {
      TokenManager.instance = new TokenManager();
    }
    return TokenManager.instance;
  }

  // 生成随机Token字符串
  private generateToken(length = 32): string {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 可用字符集
    let token = '';
    for (let i = 0; i < length; i++) {
      token += chars.charAt(Math.floor(Math.random() * chars.length)); // 随机选取字符
    }
    console.log(token); // 打印生成的令牌（实际生产环境中可能需要移除）
    return token;
  }

  // 创建新的双token（访问令牌和刷新令牌）
  public createTokens(userInfo: SessionUserInfo): TokenInfo {
    const now = Date.now(); // 当前时间戳
    const accessToken = this.generateToken(); // 生成访问令牌（默认32位）
    const refreshToken = this.generateToken(64); // 生成刷新令牌（64位，更长更安全）
    const accessTokenExpiry = now + this.ACCESS_TOKEN_TTL; // 计算访问令牌过期时间
    const refreshTokenExpiry = now + this.REFRESH_TOKEN_TTL; // 计算刷新令牌过期时间
    
    // 存储tokens和用户信息到本地存储
    localStorage.setItem(this.ACCESS_TOKEN_KEY, accessToken);
    localStorage.setItem(this.REFRESH_TOKEN_KEY, refreshToken);
    localStorage.setItem(this.ACCESS_TOKEN_EXPIRY_KEY, accessTokenExpiry.toString());
    localStorage.setItem(this.REFRESH_TOKEN_EXPIRY_KEY, refreshTokenExpiry.toString());
    localStorage.setItem(this.USER_INFO_KEY, JSON.stringify(userInfo));

    // 设置自动刷新定时器
    this.scheduleTokenRefresh();

    // 返回创建的令牌信息
    return {
      accessToken,
      refreshToken,
      accessTokenExpiry,
      refreshTokenExpiry
    };
  }

  // 获取当前存储的Token信息
  public getCurrentTokens(): TokenInfo | null {
    const accessToken = localStorage.getItem(this.ACCESS_TOKEN_KEY);
    const refreshToken = localStorage.getItem(this.REFRESH_TOKEN_KEY);
    const accessTokenExpiryStr = localStorage.getItem(this.ACCESS_TOKEN_EXPIRY_KEY);
    const refreshTokenExpiryStr = localStorage.getItem(this.REFRESH_TOKEN_EXPIRY_KEY);

    // 如果任何必要的令牌信息缺失，返回null表示无效
    if (!accessToken || !refreshToken || !accessTokenExpiryStr || !refreshTokenExpiryStr) {
      return null;
    }

    // 返回完整的令牌信息，注意将过期时间字符串转换为数字
    return {
      accessToken,
      refreshToken,
      accessTokenExpiry: parseInt(accessTokenExpiryStr),
      refreshTokenExpiry: parseInt(refreshTokenExpiryStr)
    };
  }

  // 获取存储的用户信息
  public getUserInfo(): SessionUserInfo | null {
    const userInfoStr = localStorage.getItem(this.USER_INFO_KEY);
    if (!userInfoStr) {
      return null; // 用户信息不存在，返回null
    }
    return JSON.parse(userInfoStr); // 解析并返回用户信息对象
  }

  // 验证访问令牌是否有效（未过期）
  public isAccessTokenValid(): boolean {
    const tokens = this.getCurrentTokens();
    if (!tokens) {
      return false; // 令牌不存在，无效
    }
    const now = Date.now();
    return now < tokens.accessTokenExpiry; // 当前时间小于过期时间则有效
  }

  // 验证刷新令牌是否有效（未过期）
  public isRefreshTokenValid(): boolean {
    const tokens = this.getCurrentTokens();
    if (!tokens) {
      return false; // 令牌不存在，无效
    }
    const now = Date.now();
    return now < tokens.refreshTokenExpiry; // 当前时间小于过期时间则有效
  }

  // 使用刷新令牌刷新访问令牌
  public refreshAccessToken(): TokenInfo | null {
    // 首先检查刷新令牌是否有效
    if (!this.isRefreshTokenValid()) {
      return null; // 刷新令牌无效，无法刷新
    }

    const userInfo = this.getUserInfo();
    if (!userInfo) {
      return null; // 用户信息缺失，无法刷新
    }

    // 清除旧的刷新定时器
    this.clearRefreshTimeout();

    // 创建新的访问令牌，但保留现有的刷新令牌
    const now = Date.now();
    const newAccessToken = this.generateToken();
    const newAccessTokenExpiry = now + this.ACCESS_TOKEN_TTL;

    // 更新本地存储中的访问令牌和过期时间
    localStorage.setItem(this.ACCESS_TOKEN_KEY, newAccessToken);
    localStorage.setItem(this.ACCESS_TOKEN_EXPIRY_KEY, newAccessTokenExpiry.toString());

    // 设置新的自动刷新定时器
    this.scheduleTokenRefresh();

    // 获取更新后的令牌信息并返回
    const tokens = this.getCurrentTokens();
    if (!tokens) {
      return null; // 意外情况，刷新后令牌仍不存在
    }

    return {
      accessToken: newAccessToken,
      refreshToken: tokens.refreshToken, // 保留原有的刷新令牌
      accessTokenExpiry: newAccessTokenExpiry,
      refreshTokenExpiry: tokens.refreshTokenExpiry
    };
  }

  // 安排令牌自动刷新
  private scheduleTokenRefresh(): void {
    const tokens = this.getCurrentTokens();
    if (!tokens) {
      return; // 令牌不存在，无法安排刷新
    }

    // 计算在访问令牌过期前5分钟刷新的时间点
    const refreshTime = tokens.accessTokenExpiry - (5 * 60 * 1000) - Date.now();
    
    if (refreshTime > 0) { // 确保刷新时间为正数
      this.clearRefreshTimeout(); // 清除可能存在的旧定时器
      // 设置新的定时器，到时自动刷新访问令牌
      this.refreshTimeout = window.setTimeout(() => {
        this.refreshAccessToken();
      }, refreshTime);
    }
  }

  // 清除刷新定时器
  private clearRefreshTimeout(): void {
    if (this.refreshTimeout) {
      clearTimeout(this.refreshTimeout); // 清除定时器
      this.refreshTimeout = null; // 重置定时器ID
    }
  }

  // 清除所有令牌和用户信息
  public clearTokens(): void {
    this.clearRefreshTimeout(); // 清除定时器
    // 从本地存储中移除所有相关信息
    localStorage.removeItem(this.ACCESS_TOKEN_KEY);
    localStorage.removeItem(this.REFRESH_TOKEN_KEY);
    localStorage.removeItem(this.ACCESS_TOKEN_EXPIRY_KEY);
    localStorage.removeItem(this.REFRESH_TOKEN_EXPIRY_KEY);
    localStorage.removeItem(this.USER_INFO_KEY);
  }

  // 获取访问令牌剩余有效时间（毫秒）
  public getTokenTimeRemaining(): number {
    const tokens = this.getCurrentTokens();
    if (!tokens || !this.isAccessTokenValid()) {
      return 0; // 令牌无效或不存在，剩余时间为0
    }
    return tokens.accessTokenExpiry - Date.now(); // 计算并返回剩余毫秒数
  }

  // 格式化剩余时间为友好显示字符串
  public formatTimeRemaining(): string {
    const remaining = this.getTokenTimeRemaining();
    if (remaining <= 0) {
      return '已过期'; // 令牌已过期
    }

    const minutes = Math.floor(remaining / (1000 * 60)); // 计算剩余分钟数
    const seconds = Math.floor((remaining % (1000 * 60)) / 1000); // 计算剩余秒数

    if (minutes > 0) {
      return `${minutes}分${seconds}秒`; // 格式：X分Y秒
    }
    return `${seconds}秒`; // 格式：Y秒（不足1分钟）
  }

  // 检查是否需要重新认证（登录）
  public needsReauthentication(): boolean {
    // 如果刷新令牌也过期了，需要重新登录
    return !this.isRefreshTokenValid();
  }

  // 更新活动时间（重置令牌过期时间）
  public updateActivity(): void {
    const tokens = this.getCurrentTokens();
    if (!tokens) {
      return; // 令牌不存在，无法更新
    }

    const now = Date.now();
    const newAccessTokenExpiry = now + this.ACCESS_TOKEN_TTL; // 从当前时间重新计算过期时间
    
    localStorage.setItem(this.ACCESS_TOKEN_EXPIRY_KEY, newAccessTokenExpiry.toString()); // 更新过期时间
    
    // 重新安排刷新定时器
    this.scheduleTokenRefresh();
  }
}

// 导出单例实例，使整个应用共享同一个TokenManager实例
export default TokenManager.getInstance();

